package momo.app.comms;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import momo.app.util.FileOutput;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class MsgTracker
{
	private Log log = LogFactory.getLog(MsgTracker.class);
	private Log logLostMsg = LogFactory.getLog("MoMoLostMsgLog");
	
	private ScheduledExecutorService scheduler;
	private ScheduledExecutorService checkerScheduler;
	
	private Map<String, ScheduledFuture<String>> container;
	
	public MsgTracker()
	{
		scheduler = Executors.newScheduledThreadPool(1);
		checkerScheduler = Executors.newScheduledThreadPool(1);
		container = Collections.synchronizedMap(new LinkedHashMap<String, ScheduledFuture<String>>());
		
		checkerScheduler.scheduleWithFixedDelay(
				new Runnable()
				{
					public void run()
					{
						if ( log.isDebugEnabled() ) {
							log.debug("checkScheduler starts now");
							log.debug("Size: " + container.size());
						}
						if ( logLostMsg.isDebugEnabled() ) {
							logLostMsg.debug("checkScheduler starts now");
							logLostMsg.debug("Size: " + container.size());
						}
						
						Map<String, Set<Integer>> mapping = new HashMap<String, Set<Integer>>();
						
						Iterator<String> keysIter = container.keySet().iterator();
						while ( keysIter.hasNext() )
						{
							ScheduledFuture<String> task = container.get(keysIter.next());
							
							if ( task.isCancelled() )
							{
								if ( log.isDebugEnabled() )
									log.debug("task is canceled");
								if ( logLostMsg.isDebugEnabled() )
									logLostMsg.debug("task is canceled");
								
								keysIter.remove();
							}else if ( task.isDone() )
							{
								try {
									if ( log.isDebugEnabled() )
										log.debug("task done. Getting values");
									if ( logLostMsg.isDebugEnabled() )
										logLostMsg.debug("task done. Getting values");
									
									String isLost = task.get();
									
									if ( log.isDebugEnabled() )
										log.debug("Get: \"" + isLost + "\"");
									if ( logLostMsg.isDebugEnabled() )
										logLostMsg.debug("Get: \"" + isLost + "\"");
									
									if ( isLost.length() > 0 )
									{
										StringTokenizer tokens = new StringTokenizer(isLost, ",");
										String sourceId = tokens.nextToken();
										int localSeqNo = Integer.parseInt(tokens.nextToken());
										
										Set<Integer> set = mapping.get(sourceId);
										if ( set == null )
										{
											set = new TreeSet<Integer>();
											mapping.put(sourceId, set);
										}
										set.add(localSeqNo);
									}
									
								} catch (InterruptedException e) {
									e.printStackTrace();
								} catch (ExecutionException e) {
									e.printStackTrace();
								}
								keysIter.remove();
							}else
							{
								break;
							}
						}//end while
						if ( mapping.size() > 0 )
						{
							if ( log.isDebugEnabled() )
								log.debug("<---------------- Found msg to be lost --------------------->");
							CommsManager.getInstance().msgLost(Collections.unmodifiableMap(mapping));
							
						}
					}
				},
				60,
				60,
				TimeUnit.SECONDS);
	}
	
	/**
	 * 
	 * @param sourceId
	 * @param localSeqNo
	 * @return true if this msg (w.r.t. sourceId and localSeqNo) are not being monitered
	 */
	public boolean addCheckerOnLostMsg(String sourceId, int localSeqNo)
	{
		StringBuffer buffer = new StringBuffer();
		buffer.append(sourceId);
		buffer.append(localSeqNo);
		
		if ( !container.containsKey(buffer.toString()) )
		{
			LostMsgCheckerTask task = new LostMsgCheckerTask(sourceId, localSeqNo);
			ScheduledFuture<String> schedTask = scheduler.schedule(task, 60, TimeUnit.SECONDS);
			if ( log.isDebugEnabled() )
				log.debug("Msg being monitered - Source: " + sourceId + ", SeqNo: " + localSeqNo);
			container.put(buffer.toString(), schedTask);
			return true;
		}
		return false;
	}
	
	
}//end of class MsgTracker
